package cn.edu.sgu.www.pms.util;

import cn.edu.sgu.www.pms.Pms;
import cn.edu.sgu.www.pms.annotation.AnonymityAccess;
import cn.edu.sgu.www.pms.annotation.FeignService;
import cn.edu.sgu.www.pms.entity.Permission;
import cn.edu.sgu.www.pms.mapper.ApplicationMapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 接口资源扫描工具类
 * @author 沐雨橙风ιε
 * @version 1.0
 */
@Slf4j
@Component
public class ResourceScanner {

    @Value("${spring.application.name}")
    private String service;

    private Integer serviceId;

    private static List<String> classPaths = new ArrayList<>();
    private static final List<Permission> resources = new ArrayList<>();

    private final ApplicationMapper applicationMapper;

    @Autowired
    public ResourceScanner(ApplicationMapper applicationMapper) {
        this.applicationMapper = applicationMapper;
    }

    /**
     * 扫描controller包下的目录，生成权限
     * @param basePackage controller包
     * @return List<Permission>
     * @throws ClassNotFoundException 类找不到时抛出异常
     */
    public List<Permission> scan(String basePackage) throws ClassNotFoundException {
        // 删除掉上一次的数据
        if (!resources.isEmpty()) {
            resources.clear();
        }
        if (!classPaths.isEmpty()) {
            classPaths.clear();
        }

        // 项目根路径
        String rootPath = Objects.requireNonNull(Pms.class.getResource("/")).getPath();
        String classpath = rootPath.replaceFirst("/", "");
        // controller包的路径
        String searchPath = classpath + basePackage.replace(".", "/");

        File file = new File(searchPath);

        if (!file.exists()) {
            throw new RuntimeException("指定的包路径" + searchPath + "不存在~" );
        }

        classPaths = getClassPaths(file);
        serviceId = applicationMapper.getServiceId(service);

        for(String classPath : classPaths) {
            // 得到类的全限定名
            classPath = classPath.replace(classpath.replace("/", "\\")
                    .replaceFirst("\\\\", ""), "")
                    .replace("\\", ".")
                    .replace(".class", "");
            classpath = classPath.substring(classPath.indexOf(basePackage));

            // 通过反射获取类的信息
            Class<?> type = Class.forName(classpath);

            if (type.isAnnotationPresent(Api.class)) {

                // 控制器类上的路径
                String prefix = "";
                Permission parent = new Permission();

                // 获取标注在类上的@RequestMapping注解
                RequestMapping requestMapping = type.getAnnotation(RequestMapping.class);

                if (requestMapping != null) {
                    // 获取控制器类path或者value属性的值
                    prefix = requestMapping.value().length > 0 ? requestMapping.value()[0] : requestMapping.path()[0];

                    // 以应用名_控制器类名作为权限的ID
                    parent.setId(service + "_" + type.getSimpleName());
                    parent.setUrl(prefix);
                    parent.setValue(prefix.substring(1) + ":*");
                    // 设置权限类型为：0-父权限
                    parent.setType(0);
                    parent.setRemote(0);
                    // 设置为get请求
                    parent.setMethod(0);
                    parent.setAnonymity(0);
                    parent.setServiceId(serviceId);

                    // 设置name

                    Api api = type.getAnnotation(Api.class);

                    if (api != null) {
                        // 获取控制器类上@Api注解的tags属性值
                        String name = api.tags()[0];

                        parent.setName(name);
                    }

                    resources.add(parent);
                }

                Method[] methods = type.getDeclaredMethods();

                for (Method method : methods) {
                    getClassAnnotation(method, prefix, type.getSimpleName(), parent.getId());
                }
            }
        }

        return resources;
    }

    /**
     * 得到类上面的注解信息
     * @param method Method
     * @param prefix String 控制器类上@RequestMapping注解指定的路径
     * @param controllerName 控制器名称
     * @param parentId String 父级权限ID
     */
    public void getClassAnnotation(Method method, String prefix, String controllerName, String parentId) {
        // 构建子权限
        Permission permission = new Permission();

        // 获取url
        if (method.isAnnotationPresent(RequestMapping.class)
                || method.isAnnotationPresent(GetMapping.class)
                || method.isAnnotationPresent(PostMapping.class)) {
            String url;
            String requestMethod;

            if (method.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);

                url = prefix + (requestMapping.value().length > 0 ? requestMapping.value()[0] : requestMapping.path()[0]);
                requestMethod = requestMapping.method().length > 0 ? requestMapping.method()[0].name() : "get";
            } else if (method.isAnnotationPresent(GetMapping.class)) {
                GetMapping getMapping = method.getAnnotation(GetMapping.class);

                url = prefix + (getMapping.value().length > 0 ? getMapping.value()[0] : getMapping.path()[0]);
                requestMethod = "get";
            } else {
                PostMapping postMapping = method.getAnnotation(PostMapping.class);

                url = prefix + (postMapping.value().length > 0 ? postMapping.value()[0] : postMapping.path()[0]);
                requestMethod = "post";
            }

            // 设置value
            if (method.isAnnotationPresent(ApiOperation.class)) {
                ApiOperation operation = method.getAnnotation(ApiOperation.class);

                if (operation != null) {
                    permission.setName(operation.value());
                }
            }

            // 设置默认值为：0-不允许
            permission.setAnonymity(0);

            if (method.isAnnotationPresent(AnonymityAccess.class)) {
                AnonymityAccess annotation = method.getAnnotation(AnonymityAccess.class);

                permission.setAnonymity(annotation.value() ? 1 : 0);
            }

            // 以 “应用名_控制器类名_方法名” 作为权限的ID
            permission.setId(service + "_" + controllerName + "_" + method.getName());
            permission.setUrl(url);
            permission.setValue(url.substring(1).replace("/", ":"));
            permission.setRemote(method.isAnnotationPresent(FeignService.class) ? 1 : 0);
            permission.setParentId(parentId);
            // 设置权限类型为1-子权限
            permission.setType(1);
            permission.setMethod("get".equals(requestMethod) ? 0 : 1);
            permission.setServiceId(serviceId);

            resources.add(permission);
        } else {
            log.debug("控制器类{}的{}方法上没有@XxxxMapping注解", controllerName, method.getName());
        }
    }

    private List<String> getClassPaths(File path) {
        if (path.isDirectory()) {
            File[] files = path.listFiles();

            if (files != null && files.length > 0) {
                for (File file : files) {
                    getClassPaths(file);
                }
            }
        } else {
            if (path.getName().endsWith(".class")) {
                classPaths.add(path.getPath());
            }
        }

        return classPaths;
    }

}